//
//  utils.c
//
//
//  Created by  AbbyCin on 11/1/14.
//  Copyright (c) 2014  AbbyCin. All rights reserved.
//

#include "utils.h"	

struct html {
    size_t size;
    char *page;
};

struct html make_format(const char *ori)
{
    char *head = "<html>\n<head>\n\t<meta charset='utf-8'>\n</head>\n";
    char *body_start = "<body>\n\t<pre>\n";
    char *body_end = "\n\t</pre>\n</body>";
    char *end = "\n</html>";
	struct html page_stat;
	
    page_stat.page = 
    (char *) 
	malloc(sizeof(head) + sizeof(body_start) + sizeof(body_end) + sizeof(ori) + sizeof(end));
    bzero(page_stat.page, sizeof(page_stat.page)); /* pointer not free */
    sprintf(page_stat.page, "%s%s%s%s%s", head, body_start,ori, body_end, end);
    
    page_stat.size = strlen(page_stat.page);
	
	return page_stat;
}

void do_cgi(int sockfd, char *path)
{
    size_t i;
    int j = 9; /* './cgi-bin/' 10(0-9) charactors */
    char *cmd;
    int p[2];
    char *cgi_path = "/home/angel/bin/HTML/HomePage/cgi-bin/";
    size_t buf_size = 1024;
    char header[MAX_SIZE];
    char *buf = NULL;
    size_t count = 0;
    int error = 0;
    
    struct html page;
    
    buf = (char *)malloc(buf_size);
    pipe(p);
    
    parse(path);
/*-------------- get cmd from path in format './xxx/xxx.py' -----------*/
    cmd = (char *)malloc(256);
    bzero(cmd, 256);
    
    
    cmd[0] = '.';
    
    i = strlen(path);
    
    for(i = 1; path[j] != '\0'; i++)
        cmd[i] = path[j++];
/*-------------- end -----------*/
    chdir(cgi_path);

    if(fork() == 0)
    {
        close(p[0]);
        dup2(p[1], STDOUT_FILENO);
        
        if(system(cmd) == -1)                  /* 简单、粗暴 ORZ*/
            error = -1;
    }
    
    if(error == -1)
    {
        buf ="<span style=\"color:red; font-size:60px\">No such file or directory</span>";
    }
    else
    {
        bzero(buf, 1024);
        count = read(p[0], buf, buf_size);
    }
    bzero(&page, sizeof(struct html));
	
    page = make_format(buf);
        
    sprintf(header,"HTTP/1.1 200 OK\r\n");
    sprintf(header,"%sServer: pisces for ever\r\n",header);
    sprintf(header,"%sContent-type: %s\r\n\r\n",header,"text/html");
        
    close(p[1]);
    write(sockfd, header, strlen(header));

    if(write(sockfd, page.page, page.size) == -1)
        syslog(LOG_ERR, "write cgi_output failed : %s", strerror(errno));
    
    wait(NULL);
    free(path);
    free(buf);
}

void parse(char *url)   // remove '?' 
{
    int i;
    for(i = 0; url[i] != '?'; i++);
    url[i] = '\0';
}


char *do_parse(const char *buf)
{
    char *filepath = NULL;
    char *first_line = NULL;
	char *home = "./index.html";
    int i=0;
    int j=4;
    int len = 0;
    
    for(i = 0; buf[i+1] != '\n'; i++);
    
    first_line = (char *)malloc(i);
    
    strncpy(first_line, buf, i);
    
    len = i - 12;
    filepath = (char *)malloc(i+1);
	memset(filepath, 0, i+1);
	if(i <= 14)
	{
		strcpy(filepath, home);
		return filepath;
	}
	else
	{
		//filepath[0] = '.';  /* now that added chroot(), so deprecated*/
		for(i = 0; i < len-1; i++)
			filepath[i] = first_line[j++];
	}
    free(first_line);
    return filepath;
}

void do_resolve(char filetype[], const char path[]) /* resolve http header */
{
    int i;
    char *minetype[]={
        "video/mp4",
        "audio/mp3",
        "image/jpeg", /* jpeg */
        "image/jpg", /* jpg */
        "image/png",
		"image/gif",
        "text/html",
        "text/plain"
    };

    bzero(filetype, MAX_SIZE);
    
    for(i=0; i< 8; i++)
        if(strstr(path, minetype[i]))
        {
            strcpy(filetype, minetype[i]);
            break;
        }
}


void *mymmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)
{
    void *ptr = NULL;
    
    if((ptr = mmap(addr, len, prot, flags, fd, offset)) == ((void *) -1))
    {
        syslog(LOG_CRIT,"mmap error: %s", strerror(errno));
        exit(1);
    }
    
    return ptr;
}

 /* can NOT set to 'static' internal */
ssize_t writen(int sockfd, void *filebuf, size_t n)
{
    size_t nleft = n;           /* number of left */
    ssize_t nwritten;           /* number of written */
    char *buf = filebuf;
    
    while(nleft > 0)
    {
        if((nwritten = write(sockfd, buf, nleft)) == -1)
        {
            if(errno == EINTR)
                nwritten = 0;
            else
                return -1;
        }
        
        nleft -= nwritten;
        buf += nwritten;        /* pointer's offset increase nwritten */
    }
    return n;
}

void sock_writen(int sockfd, void *filebuf, size_t n)
{
    if(writen(sockfd, filebuf, n) != n)
    {
        syslog(LOG_ERR, "write data to socket: %s", strerror(errno));
        exit(1);
    }
}

int make_list(char *dir, char **res)
{
	DIR *dirp = NULL;
	struct dirent *dp = NULL;
	int i=0;
	
	if((dirp = opendir(dir)) == NULL)
		syslog(LOG_ERR,"Can NOT open directory");
	else
	{
		while((dp = readdir(dirp)) != NULL && i < MAX_LIST)
		{
			if(dp->d_name[0] != '.')
			{
				res[i] = (char *)malloc(strlen(dp->d_name) + 1);
				strcpy(res[i], dp->d_name);
				i++;
			}
		}
	}
	
	return i;
}

void show_links(int sockfd, char **res, char *dir, int num)
{
	char header[MAX_SIZE];
	char *body;
	char *link_s = "<a href=\"";
	char *link_e = "</a>";
	int i=0; 
	struct html page;
	
	body = (char *)malloc(MAX_SIZE);
	bzero(body, MAX_SIZE);
	
	// bad way ...
	for(i=0; i<num; i++)
		sprintf(body, "%s%s%s/%s\">%s%s\n", body,link_s, dir, res[i],res[i], link_e);
	
	page = make_format(body);

	sprintf(header,"HTTP/1.1 200 OK\r\n");
	sprintf(header,"%sServer: pisces for ever\r\n",header);
	sprintf(header,"%sContent-type: %s\r\n\r\n",header,"text/html");
	
	if(write(sockfd, page.page, page.size) == -1)
		syslog(LOG_ERR, "write cgi_output failed : %s", strerror(errno));

	free(body);
}

static void run_404(int sockfd)
{
	char header[MAX_SIZE];
	struct html page;
	char *error_404 = "<p style=\"color:red; font-size:60px\">404 ORZ</span>";
	sprintf(header,"HTTP/1.1 200 OK\r\n");
	sprintf(header,"%sServer: pisces for ever\r\n",header);
	sprintf(header,"%sContent-type: %s\r\n\r\n",header,"text/html");
	
	page = make_format(error_404);
	
    if(write(sockfd, page.page, page.size) == -1)
        syslog(LOG_ERR, "write cgi_output failed : %s", strerror(errno));
}

void do_trans(int sockfd, const char *path)
{
    int fd;
    char header[MAX_SIZE];
    void *fd_cast = NULL;          /* mmap return */
    struct stat file_stat;
    char filetype[MAX_SIZE];
    char *filepath = NULL;
	
	filepath = (char *)malloc(256);
	memset(filepath, 0, 256);
    
    filepath = do_parse(path);
    if(strstr(filepath, "cgi-bin"))
	{
		do_cgi(sockfd, filepath);
	}
	else
	{
		lstat(filepath, &file_stat);
		if(S_ISDIR(file_stat.st_mode))
		{	/* not show dir by defult, so search if index file is exist in dir */
			char tmp[strlen(filepath) + strlen("/index.html")];
			sprintf(tmp, "%s/index.html",filepath);
			if((fd = open(tmp, O_RDONLY)) == -1)
			{
				char *list[MAX_LIST];
				int out;
				out = make_list(filepath, list);
				show_links(sockfd, list, filepath, out);
				for(;out--;)
					free(list[out]);
			}
			else
				goto normal;
		}
		else
		{
			fd = open(filepath, O_RDONLY);
			if(fd == -1)
			{
				//syslog(LOG_ERR, "open failed: %s", strerror(errno));
				run_404(sockfd);
				free(filepath);
				exit(EXIT_FAILURE);
			}
normal:			
			fd_cast = mymmap(0, file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);      /* cast file descriptor to memory */
			close(fd);
    
			if(strstr(filepath, "index.html"))
			{
				memset(filetype, 0, MAX_SIZE);
			strcpy(filetype, "text/html");
			}
			else
				do_resolve(filetype, filepath);
    
			free(filepath);
				/* server response header */
			sprintf(header,"HTTP/1.1 200 OK\r\n");
			sprintf(header,"%sServer: pisces for ever\r\n",header);
#ifdef __APPLE__                                                /* works on OS X */
			sprintf(header,"%sContent-length: %lld\r\n",header, file_stat.st_size);
#else                                                           /* tested on dragonfly bsd and archlinux */
			sprintf(header,"%sContent-length: %ld\r\n",header, file_stat.st_size);
#endif
			sprintf(header,"%sConnection: Keep-Alive\r\n",header);
			sprintf(header,"%sContent-type: %s\r\n\r\n",header,filetype);
    
			sock_writen(sockfd, header, strlen(header));
    
			/* write file contents; ie: send response body to client */
			sock_writen(sockfd, fd_cast, file_stat.st_size);
			close(sockfd);
    
			memset(header, 0, MAX_SIZE);                /* clean header */
    
			if(munmap(fd_cast, file_stat.st_size) == -1)
			{
				syslog(LOG_ERR, "un map memory failed: %s", strerror(errno));
				exit(1);
			}
		}
	}
}

